home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-11-22 | 56.9 KB | 1,464 lines |
-
-
-
-
- TADS: The Text Adventure Development System
- Software for implementing text adventure games
- by Michael J. Roberts
-
-
-
-
-
-
- Overview Documentation
-
-
-
-
- Copyright (c) 1992, 1996 by Michael J. Roberts.
- All Rights Reserved.
-
-
-
-
-
-
-
-
-
- ------------------------------------------------------------------------------
- Introduction
-
- This is a brief overview of TADS, a program that makes it easier to
- write your own text adventure games. TADS is offered as freeeware,
- which means that you use the software without charge.
-
- This is a summary of the information in the full TADS Author's Manual,
- which is also freely available. We prepared this information to provide
- a quick introduction to TADS.
-
-
- ------------------------------------------------------------------------------
- Ditch Day Drifter
-
- We've included a large sample game, Ditch Day Drifter, with TADS.
- The source for the game is in the file DITCH.T. We included Ditch Day
- Drifter as a sample of what TADS can do, and to help you see how you
- can go about writing your own game with TADS. After reading through
- this description of TADS, you may find it helpful to look at DITCH.T
- to see more examples of how to write TADS code.
-
- If you want to play Ditch Day Drifter, you need to compile it first.
- On MS-DOS machines, go to the TADS directory and type this:
-
- tc ditch
-
- The "tc" command runs the TADS Compiler, which converts the game
- source file into a binary executable format. Run the game with
- this command:
-
- tr ditch
-
-
- On Macintosh systems, start the compiler by double-clicking on the
- application named "TADS Compiler". The compiler will display a dialog
- box with several parameters. Click on the "Select" button next to the
- "Filename" box, then select the file DITCH.T using the normal file
- selector dialog that appears. Then, click on the "Compile" button.
- When the compiler finishes, click on the "Quit" button. Now you
- can run the game either by double-clicking on the new document
- DITCH.GAM, or by double-clicking on the application "TADS Run-Time",
- then selecting DITCH.GAM from the file selector dialog.
-
-
-
- ------------------------------------------------------------------------------
- What is TADS?
-
- TADS is a tool that makes it easier to write text adventure games.
- The system consists of a compiler, which reads source code written
- in the TADS language, checks it for errors, and converts it to an
- internal representation; and a run-time system, which reads commands
- typed by the player, and controls the interaction between the player
- and your game program.
-
- It's easier to write an adventure using TADS than with a general
- purpose language for several reasons.
-
- - The TADS language is specially designed for text adventure
- games, so the things you do most frequently in programming
- an adventure are easy to do.
-
- - The run-time system provides an excellent player command
- parser -- you don't need to worry about parsing player
- commands at all.
-
- - All of the work of saving and restoring games, undoing
- commands, controlling the on-screen display, and many
- other low-level tasks, is done automatically by the system.
-
- - TADS includes a large set of common adventure objects,
- implemented using the TADS language. You can use these
- objects as they are, or make your own customizations.
-
- - Games written with TADS are instantly portable to every
- system on which the TADS run-time system is available --
- you don't even need to recompile your game!
-
-
-
- ------------------------------------------------------------------------------
- Writing games with TADS
-
- To write a game with TADS, you start off by writing your game's
- source file with a text editor; you can use any editor that can
- produce a plain text file. Once you've written your source file,
- you compile it using the TADS compiler. If the game had no errors,
- you can run the game using the TADS run-time system. As you write
- your game, you'll probably write a little, compile it, try it out,
- then go back and add more, compile and try that, and so on.
-
-
-
- ------------------------------------------------------------------------------
- Getting Started - A Sample Game
-
- To help you get started writing your own games with TADS, we'll go
- through the implementation of a sample game. This example is similar
- to the one used in chapter one of the TADS Author's Manual.
-
- We'll start with about the simplest game possible: two rooms, and
- no items. We could start with only one room, but then there wouldn't
- be anything to do while playing the game; with two rooms, at least
- the player can move between the rooms.
-
- If you want to try running this game, create a file containing the
- program text shown below using your text editor or word processor.
- The TADS compiler will accept an ASCII file saved by any text editor.
- If you're using a word processor, you may have to choose a special
- option to save the file as plain text, without any formatting codes;
- refer to your word processor's documentation for details.
-
- Note that the lines of equal signs are just borders to show where the
- example starts and ends; use only the part between the lines of equal
- signs when creating the file.
-
- ==============================================================================
- /* This is a comment, just like in C */
- #include <adv.t> /* read basic adventure game definitions file */
- #include <std.t> /* read starting standard functions file */
-
- startroom: room /* the game always starts in startroom */
- sdesc = "Outside cave" /* the Short DESCription of the room */
- ldesc = "You're standing in the bright sunlight just
- outside of a large, dark, forboding cave, which
- lies to the north."
- north = cave /* the room called "cave" lies to the north */
- ;
-
- cave: room
- sdesc = "Cave"
- ldesc = "You're inside a dark and musty cave. Sunlight
- pours in from a passage to the south."
- south = startroom
- ;
- ==============================================================================
-
- To run this example, all you have to do is compile it with TC, the TADS
- Compiler, then run it with TR, the TADS Run-time system. If you named
- your sample program file "mygame.t", on most operating systems you can
- compile it with this command:
-
- tc mygame
-
- and you can run it with this command:
-
- tr mygame
-
- Let's look at the sample program line by line.
-
- The first line is a #include command. This command inserts another
- source file into your program. The file "adv.t" (included with the
- TADS package) is a set of basic definitions that can be used by most
- adventure games. By including adv.t in your game, you don't need to
- worry about defining words such as "the", a large set of verbs (such
- as "take", "north", and other common verbs), and many "object classes"
- (described later).
-
- The next line includes the file "std.t" (which is also included with
- the TADS package), which contains additional definitions. The reason
- for placing some definitions in the separate file std.t is that you
- will almost always want to change most of the definitions in std.t in
- a finished game, whereas the definitions in adv.t can be used unchanged
- by many games. (Even though most finished games will customize the
- definitions in std.t, the definitions are good enough to get us started
- with this sample game.)
-
- The next line says "startroom: room". This tells the compiler that
- you're defining a room named "startroom". A room is nothing special
- to TADS, but adv.t, which we previously included, defines what a room
- is. A room is one of the object classes that we mentioned earlier.
-
- The next line defines the "sdesc" for this room. The sdesc is a
- short description; for a room, it is displayed whenever a player
- enters the room. The next line is the "ldesc", or long description;
- it is displayed when the player enters the room for the first time,
- or asks for the full description with the "look" command. Finally,
- the "north" definition says that another room, called "cave", is
- reached when the player types "north" while in startroom.
-
- Now let's add a few items that can be manipulated by the player.
- We'll add a solid gold skull, and a pedestal for it to sit on.
- Add these definitions at the end of the source you've already
- created.
-
- ==============================================================================
- pedestal: surface, fixeditem
- sdesc = "pedestal"
- noun = 'pedestal'
- location = cave
- ;
-
- goldSkull: item
- sdesc = "gold skull"
- noun = 'skull' 'head'
- adjective = 'gold'
- location = pedestal
- ;
- ==============================================================================
-
- As with the "room" class, the "surface", "fixeditem", and "item" classes
- are not built in to TADS, but are defined in adv.t. Note that you can
- create an object using more than one class; in the example, the pedestal
- object is both a surface and a fixeditem. A surface is an object that
- can have other objects placed upon it; a fixeditem is an object that can't
- be picked up and carried by the player. The goldSkull object is simply
- an item, which is an object that the player can pick up and carry around.
-
- As with a room, the sdesc property gives a short description of the object;
- it should simply be the name of the object. There is no ldesc property
- defined for these objects, so they get the default long descriptions
- defined by their classes. The ldesc for an item simply says, for
- example, "It looks like an ordinary gold skull"; for a surface, it
- lists the objects sitting on the surface.
-
- Since these objects can be manipulated by the player, they must be
- associated with vocabulary words. The noun and adjective definitions
- specify the words the player can use to refer to the objects. Note that
- the sdesc and ldesc properties are enclosed in double quotes, but the
- vocabulary words are enclosed in single quotes. Note also that a noun
- or adjective can have multiple vocabulary words.
-
- The objects have another new property as well: location. This simply
- defines the object that contains the object being defined. In the case
- of the pedestal, its location is the room "cave"; since the goldSkull
- is on the pedestal, its location is the object "pedestal".
-
- Now let's add another feature to the game: let's add a trap, so that
- the player can't take the gold skull without getting killed. To do
- this, we'll replace the goldSkull definition shown above with the new
- definition below.
-
- ==============================================================================
- goldSkull: item
- sdesc = "gold skull"
- noun = 'skull' 'head'
- adjective = 'gold'
- location = pedestal
- doTake(actor) =
- {
- "As you lift the skull, a volley of poisonous
- arrows is shot from the walls! You try to
- dodge the arrows, but they take you by surprise!";
- die();
- }
- ;
- ==============================================================================
-
- The definition of doTake (which stands for Direct Object Take) has an
- argument specifying the character who is trying to take the object
- (since we have no characters besides the player, the actor will be
- the player's character, named "Me"). The system calls doTake whenever
- the player attempts to take the object. Note that this definition of
- doTake is associated with the object itself; another object could have
- a different doTake that does something entirely different. In this
- case, we simply display a message (since the message is enclosed in
- double quotes, it is simply displayed when this statement is executed),
- then call the "die" function, which is defined in std.t.
-
- You might wonder why we've waited until now to define doTake in the
- goldSkull object, or you might have just assumed that the system
- automatically knows what to do if doTake is not defined for an object.
- In fact, all objects do need a doTake definition, and the system has
- no default behavior if the definition is missing. However, since
- most objects will have a very similar doTake definition, it would be
- extremely tedious to have to type the whole definition for every
- normal object. Instead, we use something called "inheritance": by
- defining the goldSkull to be a member of the "item" class, you tell
- TADS that the goldSkull object inherits all of the definitions from
- the item class, in addition to any definitions it makes on its own.
- The item class (in adv.t) has a definition for doTake that is suitable
- for most objects. However, if an object of class "item" has its
- own definition of doTake, the one in the object takes precedence --
- it "overrides" the default definition from the class.
-
- We don't have a very good puzzle at this point, because there's no
- way the player can get the gold skull without getting killed. So,
- let's make it possible for the player to get the skull. We'll assume
- that the mechanism that the pedestal uses to set off the poisoned
- arrow trap is a weight sensor: when the pedestal has too little
- weight on it, the trap is set off. So, we'll create a rock that
- the player can use to keep the trap from going off: the player puts
- the rock on the pedestal, then can take the skull. Add the new
- definition below to the end of your source file.
-
- ==============================================================================
- smallRock: item
- sdesc = "small rock"
- noun = 'rock'
- adjective = 'small'
- location = cave
- ;
- ==============================================================================
-
- Now, we'll change the definition of doTake in the goldSkull object.
- Replace the old definition of doTake with the one below.
-
- ==============================================================================
- doTake(actor) =
- {
- if (self.location <> pedestal or smallRock.location = pedestal)
- {
- pass doTake;
- }
- else
- {
- "As you lift the skull, a volley of poisonous
- arrows is shot from the walls! You try to dodge
- the arrows, but they take you by surprise!";
- die();
- }
- }
- ==============================================================================
-
- This new doTake definition first checks to see if the object being taken
- (the special object "self", which is the object whose doTake definition
- is being executed), in this case, the gold skull, is already off the
- pedestal; if it is, we don't want anything to happen, so we "pass"
- doTake. We also pass doTake if the small rock is on the pedestal. When
- we pass doTake, the doTake definition that we inherited from our parent
- class (in this case, item) is invoked. This allows us to override a
- definition only under certain special circumstances, but otherwise
- use the default behavior. If the rock isn't on the pedestal, and the
- skull is being removed from the pedestal, the poisoned arrows are
- released as before.
-
- We've included a file named GOLDSKUL.T that includes this sample
- game, with the complete puzzle involving the gold skull and the
- pedestal. You can compile and run that file by typing these
- commands:
-
- tc goldskul
- tr goldskul
-
-
- ------------------------------------------------------------------------------
- The TADS Language
-
- If you know how to program in a "procedural" language like C or
- Pascal, most of the TADS language will probably look very familiar.
- However, the overall structure of a TADS program will probably seem
- a little strange at first.
-
- In particular, you'll probably wonder where the program begins.
- The answer is that a TADS game doesn't have a beginning in the same
- sense that a C or Pascal program does.
-
- A C program, like a program written in any procedural language,
- is a sequence of instructions. The program starts running at
- the main() function, and carries out the instructions listed
- there in the order they appear. The program may call subroutines
- or iterate through loops along the way, but control flows
- essentially sequentially through the program.
-
- A TADS program, in contrast, is simply a series of object definitions.
- Each definition specifies the behavior of a single object in the game.
- An object definition consists of a series of "properties", which are
- attributes of the object. A property can contain some simple
- information, such as the name of the object, or its weight, or can
- contain programming language code that defines the object's behavior.
-
- Obviously, the information you specify in your object definitions
- has to be used somehow -- something has to call the program code that
- you write. In TADS, it's the player command parser that does the work
- of deciding which properties to use in which objects in response to
- the player's commands.
-
- Later on, we'll describe in detail how the parser decides which properties
- to evaluate. In order to take full advantage of the power of TADS by
- creating your own types of objects, you'll need to understand how the
- parser works. However, one of the advantages of using TADS is that
- you can define objects using existing types without having to know
- everything about how those types work. So, we'll start off by showing
- you how to create some simple objects that you'll find in almost every
- adventure game.
-
-
- ------------------------------------------------------------------------------
- Creating a Room
-
- The first type of object you'll want to create in an adventure game
- is a room, which is simply a location in the game. The attributes of
- a room are typically its name, its description, and links to other
- rooms.
-
- The first thing you'll need to do in defining a room, or any TADS
- object, is to choose its object name. This is not a name that is
- ever displayed to or used by the player -- it is a purely internal
- name that you use to refer to the object within the TADS program.
- It is similar to the name of a variable in C or Pascal. In TADS,
- the name of an object must start with a letter, and can consist of
- letters, numbers, and underscores.
-
- Here's a sample room definition.
-
- nsCrawl: room
- sdesc = "North-South Crawl"
- ldesc = "You're in a narrow, low passage connecting
- caves to the north and south. A cold, damp
- breeze comes from the north."
- north = iceRoom
- south = roundRoom
- ;
-
- The first line of the object definition gives the object its name,
- "nsCrawl", and specifies the type of the object. Note that the case
- of the letters in the name is significant -- "nsCrawl" is a different
- name than "NSCrawl" or "nscrawl".
-
- The next line specifies a property of the object -- the sdesc, or
- short description. It says that the property sdesc is the string
- "North-South Crawl". The sdesc is displayed whenever the player
- enters the room, and is also used on the status line.
-
- In TADS, double-quoted strings are special: they mean that the
- string is to be displayed whenever the property is evaluated.
- Double-quoted strings are similar to using a PRINT statement in
- other languages. So, whenever the sdesc property of the nsCrawl
- object is evaluated, the string "North-South Crawl" will be displayed
- to the player.
-
- The next line defines the ldesc, or long description, property.
- This is the full description of the room that is displayed when
- the player enters the room for the first time, or whenever the player
- uses the "look" command to ask for a full description. Note that
- this property's value is a double-quoted string, just like the sdesc,
- to indicate that the string is displayed whenever the ldesc is
- evaluated.
-
- The next two lines specify the directional properties north and south.
- These properties are set to refer to other room objects. They specify
- that the player will end up in the room defined by the object
- iceRoom by going north, and in roundRoom by going south. Note that
- other directions are not specified -- this simply means that the player
- can't go in any other directions. The other possible direction properties
- are east, west, up, down, in, out, ne, se, nw, and sw (the last four
- stand for northeast, southeast, northwest, and southwest, respectively).
-
- The final line of the room definition is a semicolon, which tells TADS
- that the object definition is finished.
-
-
- ------------------------------------------------------------------------------
- Creating Items
-
- In addition to rooms, you'll want to create objects that the player
- can pick up and carry around. These are objects of type "item".
- When you define an item, you'll need to specify another set of
- properties: its short and long descriptions (just like rooms),
- its location (which says where the object is originally situated),
- and vocabulary words that specify how the player can refer to the
- object. Here are a couple of item definitions.
-
- dollar: item
- location = nsCrawl
- sdesc = "one-dollar bill"
- noun = 'dollar' 'bill'
- adjective = 'one' 'one-dollar' '1' 'dollar'
- ;
-
- rope: item
- noun = 'rope'
- sdesc = "rope"
- ldesc = "It's about 50 feet long, and seems quite strong; it's
- probably capable of handling several hundred pounds."
- location = backpack
- ;
-
- The first thing to notice here is that vocabulary words are enclosed
- in single quotes, not double quotes. Recall that double-quoted strings
- are displayed whenever evaluated; this is obviously not desirable for
- vocabulary words. So, always specify vocabulary words with single quotes.
-
- You may also notice that more than one single-quoted string can be
- used with a vocabulary property. This is a unique feature of vocabulary
- properties (the vocabulary properties are noun, adjective, plural,
- verb, preposition, and article). When you use more than one word
- in a vocabulary property, it simply creates multiple synonyms that can
- be used to refer to the object.
-
- Note that the properties can be defined in any order. Note also that
- you don't need to specify every property; for example, if the ldesc
- property is not specified with an item, a default message ("It looks
- like an ordinary one-dollar bill") is displayed. However, practically
- every item will define the properties sdesc, location, and noun.
-
- The location property specifies the original location of the object.
- In many cases, the location will refer to a room object. However, it
- can also refer to another game item; in these cases, the item should
- be a container, as described below.
-
-
- ------------------------------------------------------------------------------
- Containers
-
- You will often want to create items that can contain other items.
- To do this, simply create an item of type "container" rather than
- "item". In other respects, a "container" object is the same as
- an "item", and you specify the same set of properties. Here's an
- example.
-
- backpack: container
- sdesc = "backpack"
- location = maintenanceRoom
- noun = 'backpack' 'pack' 'bag'
- adjective = 'back'
- ;
-
- In addition to container objects, you can create "openable" objects.
- These are the same as containers, except that they can be opened and
- closed. When you create a container, there's a new property,
- "isopen", that specifies whether the object is open or closed.
- If you want the object to be closed initially, set isopen = nil.
- Here's an example.
-
- toolbox: openable
- sdesc = "tool box"
- location = maintenanceRoom
- noun = 'toolbox' 'box'
- adjective = 'tool'
- isopen = nil
- ;
-
-
- ------------------------------------------------------------------------------
- Fixed Items
-
- You will frequently want to create items in the game that the player
- can't pick up and carry around, but that are permanent features of
- a room. For example, if you create a bedroom, you'll probably want
- to have a bed and maybe a dresser in the room. It doesn't make sense
- for the player to be able to carry off the furniture.
-
- To create an item that the player can't carry, create an object of
- type "fixeditem". You define fixeditem objects that same way that
- you define item objects; the only difference is that fixeditem objects
- can't be carried off by the player.
-
- Here's an example.
-
- bed: fixeditem
- location = bedroom
- noun = 'bed'
- sdesc = "Bed"
- ;
-
-
- ------------------------------------------------------------------------------
- Inheritance and ADV.T
-
- Up to now, we've been describing the various types of objects that
- you can create, and the properties that you have to define when
- creating those objects. It may appear that these object types are
- somehow built in to TADS. In fact, none of these types are special;
- they're all defined using the TADS language.
-
- The objects we've seen so far -- room, item, container, openable,
- fixeditem -- are defined in the file ADV.T. This file is a set of
- object type definitions that are suitable for most games, but they're
- not at all special, so you can take them or leave them as you choose.
- You could even completely rewrite ADV.T if you want your game to
- behave differently.
-
- Note that you can create objects using the types defined in ADV.T
- (or in your own program) very easily -- as though the types were
- somehow built in to TADS. The trick that makes this possible is
- called "inheritance". The TADS language allows you to define an
- object so that it inherits all of the properties of another object.
- This is what you're doing when you create an object of type room:
- you're telling TADS that it should create a new object exactly like
- the object "room" that it's already seen, except that you want to
- add some new properties of your own. If you look at ADV.T, you'll
- see that the definition of the room object is very complex; however,
- you can create objects of type room without having to know very
- much about the room object itself -- all you need to know is what
- properties you need to add to the new object.
-
- ADV.T defines many more objects than we've talked about so far.
- You should refer to ADV.T for complete information on the
- standard adventure objects, and how to use them. Here's a brief
- list.
-
- nestedroom -- a room within another room
- chairitem -- a chair (something the player can sit on)
- beditem -- a bed (something the player can lie down on)
- thing -- low-level item class (not normally used directly)
- item -- a simple item the player can pick up and carry
- lightsource -- something that provides light
- hiddenItem -- an object that starts off hidden within another object
- hider -- low-level hider class (not used directly)
- underHider -- an object that can have objects hidden under it
- behindHider -- an object that can have objects hidden behind it
- searchHider -- an object that can have objects hidden within it
- fixeditem -- an immovable item
- readable -- something the player can read
- fooditem -- something the player can eat
- dialItem -- something the player can turn to various values
- switchItem -- something the player can turn on and off
- room -- a location in the game
- darkroom -- a location without any lighting of its own
- Actor -- a character in the game
- moveableActor -- a character that the player can pick up and carry
- follower -- a psuedo-object that can be used to follow actors
- basicMe -- a set of definitions suitable for most player actors
- decoration -- an item that serves no purpose other than decoration
- buttonItem -- something the player can push
- clothingItem -- something the player can wear
- obstacle -- something that blocks travel
- doorway -- one side of a door
- lockableDoorway -- a door that can be locked
- vehicle -- an item that can be used for travel
- surface -- an item that can have objects placed upon it
- container -- an item that can have objects placed within it
- openable -- a container that can be opened and closed
- qcontainer -- a container that doesn't list its contents automatically
- lockable -- an openable that can be locked and unlocked
- keyedLockable -- a lockable that needs a key to be locked and unlocked
- keyItem -- an item that can lock and unlock a keyedLockable
- transparentItem -- an object whose contents are visible
- basicNumObj -- definition suitable for numObj in most games
- basicStrObj -- definition suitable for strObj in most games
- deepverb -- a verb object
- travelVerb -- a verb object for travel verbs
- sysverb -- a "system" verb
- Prep -- a preposition object
-
- Each object in ADV.T is fully described in a comment preceding its
- definition. The comments tell what properties you need to define
- when creating an object of each type, and how the object will behave.
-
-
- ------------------------------------------------------------------------------
- Methods
-
- We mentioned earlier that a property can contain a simple value,
- such as a string or a number, or programming code that defines its
- behavior. When a property contains code, it is called a "method".
- With the exception of vocabulary properties, any property can
- contain programming code instead of a simple value.
-
- Programming code starts with a left brace ("{"), and ends with
- a matching right brace ("}"). Within the braces, you can use
- programming language statements that create local variables,
- call functions and methods, assign values to properties and
- variables, and control flow with loops and conditionals. The
- programming code used in TADS methods looks similar to the C
- language, but there are some differences.
-
- Here's an example of an object incorporating a method. In this
- case, the object defines its ldesc property using a method,
- rather than a simple double-quoted string value, so that the
- property can be sensitive to the state of a window.
-
- attic: room
- sdesc = "Attic"
- ldesc =
- {
- "You're in a large, dusty attic. Old cobwebs hang
- from the rafters. At the north end of the room is
- a window, which is ";
- if (atticWindow.isopen)
- "open";
- else
- "closed";
- ". A ladder leads down.";
- }
- down = hallway
- ;
-
- The ldesc property displays a message that depends on whether the
- window is open or closed. For example, if the window is open, the
- room's long description would look like this:
-
- You're in a large, dusty attic. Old cobwebs hang from the
- rafters. At the north end of the room is a window, which
- is open. A ladder leads down.
-
-
- ------------------------------------------------------------------------------
- Expressions
-
- TADS expressions are entered in algebraic notation. For example,
- to add two numbers, you would code an expression like this:
-
- 3 + 4
-
- You can use parentheses to change the order of evaluation of the
- parts of an expression. For example, since multiplication has higher
- precedence than addition, the expression 3 + 4 * 5 evaluates to 23;
- however, if you write this as (3 + 4) * 5, its value is 35, because
- the addition is performed before the multiplication.
-
- If you want to assign a value, you use the operator ":=" (a colon
- followed immediately by an equals sign, without any intervening spaces).
- For example, to assign the value nil to the isopen property of the
- object atticWindow, you'd code this:
-
- atticWindow.isopen := nil;
-
- The period (or "dot") operator, ".", is used to take a property of
- an object. In the example above, it specifies the isopen property of
- the object atticWindow. You can use the value of a property in an
- expression, or assign a new value to a property, using this operator.
-
- Here are the TADS operators, shown in the order of evaluation.
-
- & Takes the "address" of a function or property.
-
- . Takes a property of an object: obj.prop evaluates property
- prop of object obj.
-
- [] List indexing. If var contains the list [5 4 3 2 1], then
- var[2] is 4, var[4] is 2, var[5] is 1, and so on.
-
- ++ Increment a variable that contains a number, assigning the
- incremented value back to the variable. If var contains
- 5, it will contain 6 after var++ is evaluated.
-
- -- Decrement.
-
- not Logical negation. not true is nil, and not nil is true.
-
- - Arithmetic negation (when used as a unary prefix operator).
-
- * Numeric multiplication: 5 * 6 is 30.
-
- / Numeric integer division: 30 / 5 is 6. Note that any
- fractional part of the division is discarded, so 5 / 2 is 2.
-
- + Numeric addition: 3 + 4 is 7.
- String concatenation: 'hello' + 'goodbye' yields 'hellogoodbye'.
- List concatenation: [1 2] + [3 4] yields [1 2 3 4].
- List extension: [1 2] + 3 yields [1 2 3].
-
- - Numeric subtraction: 10 - 3 is 7.
- List removal: [1 2 3] - 2 yields [1 3].
-
- = Equality comparison; a = b yields true if the value of a
- is the same as the value of b, nil otherwise. Note that
- the types of a and b must be suitable for comparison; you
- can compare two numbers, strings, lists, objects, or
- truth values (true and nil); but comparing a number to a
- string is meaningless, and results in a run-time error.
-
- <> Inequality comparison; a <> b is true if a is not equal
- to b, nil otherwise.
-
- > Greater than comparison. You can compare the magnitude
- of two numbers or two strings.
-
- < Less than comparison.
-
- >= Greater than or equal.
-
- <= Less than or equal.
-
- and Logical product: a and b is true if both a and b are true,
- nil otherwise. Note that if a is nil, b is not evaluated,
- because the expression's result is nil regardless of the value
- of b.
-
- or Logical sum: a or b is true if either a or b is true, or
- if both are true. Note that if a is true, b is not evaluated.
-
- ? : Conditional operator: cond ? a : b yields the value of a
- if cond is true, b if cond is nil. Note that only one of
- a or b is evaluated.
-
- := Assignment.
-
- += Add and assign: the value of the variable or property on
- the left has the value of the expression on the right added
- to it. a += b is equivalent to a := a + b.
-
- -= Subtract and assign.
-
- *= Multiply and assign.
-
- /= Divide and assign.
-
- , Conjunction: the expression on the left of the comma is
- evaluated, then the expression on the right is evaluated
- and used as the value of the entire conjunction expression.
- So, the value of (a, b) is b.
-
-
- ------------------------------------------------------------------------------
- Self
-
- Within a method, there's a special pseudo-object called "self" that
- lets you determine the object whose method is being evaluated. This
- may sound pretty useless, but consider a situation in which you're
- defining an object that inherits from another object.
-
- book: item
- sdesc =
- {
- "The book is "; self.color; ". ";
- }
- ;
-
- redbook: book
- color = "red"
- ;
-
- bluebook: book
- color = "blue"
- ;
-
- Here, the superclass object, book, can define a generic sdesc method
- which automatically adjusts to the individual subclass objects.
- When redbook.sdesc is evaluated, the actual sdesc code that is
- executed is inherited from book; however, since it is redbook.sdesc
- that is being evaluated, self is set to redbook, so when book.sdesc
- evaluated self.color, "red" is displayed. Likewise, when bluebook.sdesc
- is evaluated, self is set to bluebook, so self.color displays "blue".
- The classes defined in ADV.T make extensive use of this mechanism
- to allow them to define generic attributes of the classes which
- automatically customize themselves to the objects you create in
- your game, even though the authors of ADV.T couldn't possibly
- have known anything about your objects beforehand.
-
-
- ------------------------------------------------------------------------------
- When do Methods Run?
-
- Before we talk about how to write TADS methods in any more detail, we
- should look at how the TADS parser works, because it's the parser that
- decides what methods to call.
-
- When the player types a command to TADS, the parser breaks the command
- into a series of words. The parser then looks through its tables of
- vocabulary words to see what objects are associated with those words;
- remember that the special properties verb, noun, plural, adjective,
- article, and preposition associate an object with a set of vocabulary
- words. Once the parser has determined which objects are involved in
- the command, it calls a series of methods in those objects; these
- methods do all the work of processing the command. Since the methods
- are defined in the objects, you can change almost any aspect of the
- behavior of a TADS game; however, thanks to inheritance, you don't
- have to change anything if you don't want to -- you can use the
- definitions from ADV.T as they are, filling in only the necessary
- descriptive text and properties.
-
- The command is associated with a set of objects; each object is
- classified according to its function in the command. The objects
- are the actor, the verb, an optional set of direct objects, and
- an optional preposition and indirect object. If the player didn't
- direct the command to a different character (for example: "floyd,
- give me the ball"), the player-actor, Me, is assumed.
-
- The sequence of method calls is shown below. The actual objects
- making up the command are substituted for the items in <angle
- brackets>. If one of the objects is not used in the command,
- the corresponding object parameter will be nil (for example,
- if there is no direct object, <dobj> will be replaced by nil).
-
- In the list below, you'll also see something named <verb-prefix>.
- This is a special string that is defined in the deepverb objects;
- we'll discuss this below. If the <verb-prefix> is, for example,
- 'Take', then the method meant by do<verb-prefix> is doTake.
-
- <actor> . roomCheck( <verb> )
- <actor> . actorAction( <verb>, <dobj>, <prep>, <iobj> )
- <actor> . location . roomAction( <actor>, <verb>, <dobj>, <prep>, <iobj> )
-
- If an indirect object was specified:
- <dobj> . verDo<verb-prefix>( <actor>, <iobj> )
- If no output resulted from verDo<verb-prefix>:
- <iobj> . verIo<verb-prefix>( <actor> )
- If no output resulted from verIo<verb-prefix>:
- <iobj> . io<verb-prefix>( <actor>, <dobj> )
- Else if a direct object was specified:
- <dobj> . verDo<verb-prefix>( <actor> )
- If no output resulted from verDo<verb-prefix>:
- <dobj> . do<verb-prefix>( <actor> )
- Else:
- <verb> . action( <actor> )
-
- Run each daemon that is currently active
- Run and remove each fuse that has burned down to zero turns
-
- The <verb-prefix> is read from the verb object when a direct or
- indirect object is present. The <verb-prefix> is a string specified
- by the property doAction when only a direct object is present, or
- from the appropriate ioAction when both a direct and an indirect object
- are present. For example, consider this verb object:
-
- takeVerb: deepverb
- verb = 'take'
- sdesc = "take"
- doAction = 'Take'
- ioAction(outofPrep) = 'TakeOut'
- ioAction(awayPrep) = 'TakeAway'
- ;
-
- If the command is "take ball", only a direct object is present, so
- the doAction property is used, and hence the <verb-prefix> is 'Take'.
- This means that the methods called in the direct object will be
- verDoTake and doTake. In this case, the sequence of methods that
- the parser calls would be:
-
- Me.roomCheck(takeVerb)
- Me.actorAction(takeVerb, ball, nil, nil)
- Me.location.roomAction(Me, takeVerb, ball, nil, nil)
- ball.verDoTake(Me)
- if no ouput resulted from verDoTake:
- ball.doTake(Me)
-
- If the command is "take ball out of box", the ioAction property
- matching the preposition "out of" is used; this is 'TakeOut'. So,
- the properties called in the objects are verDoTakeOut, verIoTakeOut,
- and ioTakeOut. In this case, the sequence of methods called by
- the parser would be:
-
- Me.roomCheck(takeVerb)
- Me.actorAction(takeVerb, ball, box, outofPrep)
- Me.location.roomAction(Me, takeVerb, ball, box, outofPrep)
- ball.verDoTakeOut(Me, box)
- If no output resulted from verDoTakeOut:
- box.verIoTakeOut(Me)
- If no output resulted from verIoTakeOut:
- box.ioTakeOut(Me, ball)
-
- This sequence may look terribly complicated, but generally you won't
- have to customize or even think about very much of it. In almost
- every case, you'll be able to achieve the effect you want by customizing
- the verDo<prefix>, do<prefix>, verIo<prefix>, and io<prefix> methods.
-
- Note that the verDo<prefix> and verIo<prefix> methods are intended
- to be used to verify that the object can be used in this command,
- and the do<prefix> and io<prefix> methods are supposed to actually
- carry out the command. The way this works is that the verDo<prefix>
- and verIo<prefix> methods should display an error message if the
- object cannot be used for this command, and should do nothing at
- all if the object is usable. This may sound like a strange way
- to decide whether the object is valid, but it makes it extremely
- convenient to write these methods -- all you have to do is display
- an error message if appropriate during verification.
-
-
- ------------------------------------------------------------------------------
- Programming Statements
-
- Within a method, or within a function, you can use a number of
- statements that control execution.
-
-
- local <variable-list> ;
-
- This statement can be used only at the very start of a block
- of code (that is, immediately following an open brace). The
- "local" statement defines a list of variables that are private
- to the block of code; these variables cannot be used outside
- of the block. Variable names follow the same rules as other
- identifiers, such as object and property names: they must
- start with a letter, and consist of letters, numbers, and
- underscores.
-
- Example:
-
- ldesc =
- {
- local cnt, loc;
-
- cnt := 0;
- loc := nil;
- }
-
-
- return <expression> ;
-
- Return to the caller. The <expression> is evaluated, and the result
- is supplied to the caller as the value of the method or function.
- Control is returned to the caller at the point immediately following
- the call to the current method or function; no further statements
- in the current method or function are executed.
-
- return ;
-
- Return to the caller without providing a value.
-
- if ( <expression> ) <statement1> else <statement2>
-
- Evaluate the <expression>; if the value is not nil or 0, execute
- <statement1>. Otherwise, execute <statement2>. Note that the
- entire "else" clause is optional; if it is not provided, and the
- value of <expression> is nil or 0, execution resumes following
- <statement1>.
-
- Note that <statement1> and <statement2> can either be a single
- statement, or can be a series of statements enclosed in braces.
-
- Example:
-
- if (torch.islit)
- {
- "You suddenly realize that the odor was coal gas,
- and that you're carrying a burning torch. You
- try to retreat, but it's too late; the torch
- ignites the gas, resulting in a terrible explosion.";
- die();
- }
- else
- "You realize that the odor was coal gas.
- Fortunately, there's no open flame here.";
-
-
- switch ( <expression> )
- {
- case <constant1>: <statements1>;
- case <constant2>: <statements2>;
- default: <statements3>;
- }
-
- This statement allows you to choose a number of execution
- paths, based on the value of an expression. The <expression>
- is evaluated, then compared to the various constants at the case
- labels. If a case label matches the value of the expression,
- execution resumes at the statement following that case label.
- If no case label matches the value, and a default label is
- present, execution resumes following the default. The default
- label is optional.
-
- Note that execution is not interrupted by hitting another
- case label, but just continues into the statements following it.
- Note also that a case label need not have any statements at all
- following it. If you want to exit the switch statement, you
- must explicitly code a break statement; this causes execution
- to resume following the end of the switch statement.
-
- Example:
-
- switch(x)
- {
- case 1:
- case 2:
- "x is 1 or 2";
- break;
-
- case 3:
- "x is 3";
- /* note the lack of a break, so we fall through to next case */
-
- case 4:
- "x is 3 or 4";
- break;
-
- default:
- "x is 5 or more";
- }
-
-
- while ( <expression> ) <statement>
-
- Execute <statement> repeatedly as long as the value <expression>
- is not nil or 0. The <expression> is evaluated before each
- execution of <statement>, so if the value of <expression> is
- nil or 0 before the first execution of the loop, the <statement>
- is not executed at all. As with the if statement, the <statement>
- can be either a single statement, or a block of statements enclosed
- in braces.
-
- Example:
-
- while (cnt < length(lst))
- {
- "The next item is: "; say(lst[cnt]); "\n";
- ++cnt;
- }
-
- do <statement> while ( <expression> );
-
- This statement is similar to the while statement, but evaluates
- <expression> after each iteration of the loop. Thus, the <statement>
- is always executed at least once, since the <expression> is not
- tested for the first time until after the first iteration of the
- loop.
-
- Example:
-
- do
- {
- "cnt = "; say(cnt); "\n";
- --cnt;
- } while (cnt > 0);
-
- for ( <init-expr> ; <cond-expr> ; <reinit-expr> ) <statement>
-
- This is a more general form of loop. First, the <init-expr> is
- evaluated; this is done only once, before the loop starts iterating.
- For each iteration of the loop, TADS first evaluates the <cond-expr>.
- If it is nil or 0, the loop terminates; otherwise, <statement> is
- executed. Finally, <reinit-expr> is evaluted. Note that any
- of the expressions may be omitted; if the <cond-expr> is not
- present, it is as though the expression were always true.
-
- The for statement is often more convenient to code, but it
- is always possible to write an equivalent loop using the
- while statement.
-
- Example:
-
- for (cnt := 1 ; cnt < length(lst) ; ++cnt)
- {
- "the next element is: "; say(lst[cnt]); "\n";
- }
-
- break;
-
- Exits from a while, do-while, or for loop, or from a switch
- statement. Control immediately resumes following the end of
- the loop or switch. The break statement is useful when the
- condition for exiting a loop is most conveniently calculated
- somewhere in the middle of the loop's processing.
-
- continue;
-
- Returns to the beginning of a while, do-while, or for loop.
- The statements following the continue statement are skipped
- for this iteration of the loop. In a for loop, the <reinit-expr>
- is evaluated after a continue statement.
-
- pass <property-name>;
-
- When a method is executing, and the method has overridden a
- method in a superclass object, the pass statement will cause
- control to be passed to the superclass method that the current
- method overrides. Note that execution never returns to the
- current method after a pass statement. Note also that the
- <property-name> must match the name of the current property.
-
- exit;
-
- Terminate all processing for the current player command,
- and skip everything up to the daemons and fuses. This
- statement is used when the current method has done everything
- necessary to finish the command, and no further processing
- is desirable.
-
- abort;
-
- Terminate all processing for the current player command,
- skip the daemons and fuses, and go on to the next command.
- This statement is normally used by "system" verbs, such as
- "save" and "restore", to prevent any time from passing (time
- in the game is handled by the fuses and daemons).
-
- askdo;
-
- Abort the current command, and ask the player to specify a
- direct object.
-
- askio( <prep-object> );
-
- Abort the current command, supply <prep-object> as the preposition
- for the command, and ask the player to specify an indirect object.
-
-
- ------------------------------------------------------------------------------
- Built-in Functions
-
- TADS has a number of built-in functions that you can call from
- your game program. Some of these functions simply provide useful
- utility functions, while others affect execution of the game.
- A brief description of each function is shown below.
-
- askfile(prompt) - asks the player for a filename; "prompt" is a
- string (single-quoted) that specifies a prompt to display to the
- player. Where appropriate, the standard system file selector is
- used to ask the player for the file.
-
- caps() - forces the next character displayed to be capitalized.
-
- car(list) - returns the first element of a list. For
- example: car([1 2 3]) returns 1.
-
- cdr(list) - returns a list with its first element removed. For
- example: cdr([1 2 3]) returns [2 3].
-
- cvtnum(str) - converts a (single-quoted) string containing the
- textual representation of a number to a numeric value. For
- example, cvtnum('1234') returns 1234.
-
- cvtstr(num) - converts a number to a string containing a textual
- representation of the number. For example, cvtstr(100) returns '100'.
-
- datatype(val) - returns the datatype of the "val" (after evaluating
- the value). The return values are:
-
- 1 - number
- 2 - object
- 3 - string
- 5 - nil
- 7 - list
- 8 - true
- 10 - function pointer
- 13 - property pointer
-
- defined(obj, &prop) - returns true if the object "obj" defines or
- inherits property "prop", nil otherwise.
-
- find(value, target) - returns the offset of "target" within "value".
- If "value" is a list, this function returns the index of the "target"
- within the list, or nil if it does not occur in the list. For
- example, find([5 4 3], 4) returns 2. If "value" is a string,
- this function returns the offset of the substring "target", or
- nil if there is no such substring. For example, find('abcdef', 'cde')
- returns 3.
-
- firstobj() - begins a loop over all non-class objects in a game.
- Returns an object. See nextobj(obj).
-
- firstobj(cls) - begins a loop over all non-class objects with
- superclass "cls". See nextobj(obj, cls).
-
- getarg(num) - return the value of argument number "num" to the current
- method or function.
-
- incturn() - increments the turn counter, which moves all fuses
- one turn closer to firing. Normally, this function is called
- by a daemon function once per turn.
-
- input() - allows the user to enter a line of text, and returns
- the value as a string.
-
- isclass(obj, cls) - returns true if "obj" inherits from superclass
- "cls", nil otherwise.
-
- length(val) - returns the number of characters in a string, or
- the number of elements in a list.
-
- logging(val) - if "val" is a string, creates the file named by
- the string, and logs all text displayed on the screen to the file.
- If "val" is nil, closes the current log file and stops logging.
-
- lower(str) - returns a string consisting of all of the characters
- of "str" converted to lower case.
-
- nextobj(obj) - returns the object following "obj", in an arbitrary
- order, or nil if the last object has been reached. Every non-class
- object in the game will be returned exactly once by a loop such as
- this:
-
- local obj;
- for (obj := firstobj() ; obj ; obj := nextobj(obj)) /* ... */;
-
- nextobj(obj, cls) - returns the next object following "obj"
- with superclass "cls", or nil if the last such object has been
- reached. Every non-class object in the game with superclass cls
- will be returned by a loop such as this:
-
- local obj;
- for (obj := firstobj(cls) ; obj ; obj := nextobj(obj, cls)) /* ... */;
-
- notify(obj, &prop, turns) - establish a notifier. The property "prop"
- of object "obj" (i.e., obj.prop) is called once after the number of turns
- specified by "turns" has elapsed. If "turns" is zero, obj.prop is called
- after every turn.
-
- proptype(obj, &prop) - returns the type of property "prop" in object
- "obj", without evaluating the property. If the property contains
- method code, the code is not called by this function, so the return
- type of the property cannot be determined; instead, proptype simply
- returns 6, to indicate that the property contains method code. The
- return values are:
-
- 1 - number
- 2 - object
- 3 - single-quoted string
- 5 - nil
- 6 - code
- 7 - list
- 8 - true
- 9 - double-quoted string
- 10 - function pointer
- 13 - property pointer
-
- quit() - end the game.
-
- rand(lim) - return a random number from 0 to "lim", inclusive.
-
- randomize() - seed the random number generator with a value chosen
- by the system. This function is called only once, at the beginning
- of the game, to choose a random number seed. If this function is
- not called, the sequence of numbers returned by rand() is the same
- each time the game is played.
-
- remdaemon(function, value) - remove a daemon previously set by
- the setdaemon() built-in function. The daemon set with the same
- values of "function" and "value" will be removed; if no such daemon
- is found, a run-time error is reported.
-
- remfuse(function, value) - remove a fuse previously set
- by the setfuse() built-in function.
-
- restart() - start the game over from the beginning.
-
- restore(filename) - restore the game position stored in the file
- named by the string "filename".
-
- save(filename) - save the current game position to the file
- named by the string "filename".
-
- say(value) - display the value, which can be a single-quoted string
- or a number.
-
- setdaemon(function, value) - set a daemon. "function" will be
- called once after every turn, with "value" as its argument. "value"
- is an arbitrary value that is provided to allow you to pass
- any information to the daemon that it may need. The daemon
- function should be defined prior to its use in setdaemon().
-
- setfuse(function, time, value) - set a fuse. "function" will be
- called after the number of turns specified in "time" has elapsed.
- "value" is an arbitrary value that is passed to the function as
- its argument; you can specify any value that you find useful here.
-
- setit(obj) - set "it" to the specified object. This changes the
- object that the player refers to with the word "it".
-
- setscore(score, turns) - set the score displayed on the status line.
- "score" and "turns" are numbers.
-
- setscore(str) - specify an arbitrary single-quoted string that will
- be displayed on the status line in place of the normal score/turns
- indicator.
-
- substr(str, ofs, len) - returns the substring of "str" starting
- at offset "ofs" and going for "len" characters. For example,
- substr('abcdef', 3, 2) returns 'cd'.
-
- undo() - undoes one turn.
-
- unnotify(obj, &prop) - remove the notifier previously set on obj.prop.
- If there is no such notifier, a run-time error is reported.
-
- upper(str) - returns the string "str" with its characters converted
- to upper-case.
-
- yorn() - waits for the player to answer YES or NO. Returns
- 1 if the player typed YES, 0 if the player typed NO, and -1 if
- the player typed anything else. Note that only the first
- character of the player's response is checked, and the response
- can be upper- or lower-case.
-
-
- ------------------------------------------------------------------------------
- Where to go from Here
-
- There's a lot more to TADS than we are able to describe here.
- To learn more, you can start by looking at the example game
- "Ditch Day Drifter" that we've included with the TADS software
- distribution. While this game doesn't do everything that you can
- do with TADS (in fact, since TADS is so flexible, it doesn't even
- really scratch the surface), it does contain examples of many
- common adventure game situations that may help you in designing
- your own game.
-
- For full details on TADS, you should obtain the TADS Author's
- Manual. This is freely available in electronic format; you should
- be able to find it the same place you found TADS. The Author's
- Manual includes:
-
- - An overview of the concepts used in the TADS programming
- language.
-
- - A detailed description of the TADS programming language,
- including how to define functions and objects, how to write
- procedural code, and how to write expressions.
-
- - How to use the TADS Debugger, a powerful source-level
- debugger for TADS programs.
-
- - Descriptions of all of the built-in functions.
-
- - Descriptions of the objects in ADV.T, and how to use them.
-
- - An introduction to object-oriented programming.
-
- - How the player command parser works: how it reads commands,
- disambiguates nouns, and calls your program to carry out
- commands.
-
- - Descriptions of the special properties that the system
- calls in your game objects.
-
- - Descriptions of the special objects that your game
- program must provide.
-
- - Explanations of system error messages.
-
- - Details on how to write and use external functions written
- in a language such as C.
-
- - How to use the compiler and run-time system.
-
- - Advice on the what to do (and what not to do) when designing
- a game to make your game more fun to play.
-
-
- In addition, the TADS Author's Manual contains a large number
- of detailed examples that show you how to write your own games.
- The chapter "Getting Started with TADS" shows you how to design
- a game, and how to turn your design into a TADS program. The
- chapter "Advanced TADS Techniques" provides 40 pages of examples
- with full explanations, showing how to implement a wide variety
- of game situations, such as:
-
- - Creating your own verbs
-
- - Using doors and other obstacles
-
- - Creating vehicles
-
- - Hiding objects
-
- - Creating non-player characters
-
-
-
- ABOUT HIGH ENERGY SOFTWARE
- --------------------------
-
- High Energy Software formerly distributed TADS as a shareware system,
- but is no longer in business. Mike Roberts, the developer of TADS,
- is now maintaining and distributing the system as freeware. We have
- made an effort to remove references from these files to High Energy
- Software and its former telephone and other contact information,
- since all of those numbers and addresses are no longer in service.
- If we inadvertantly left any such references intact, please disregard
- them, since they're oversights.
-
-